#ifndef __WAVE_CODEC_H__
#define __WAVE_CODEC_H__
//////////////////////////////////////////////////////////////////////////
// codec

_declspec(selectany) SHORT arrIndexAdjust[] = {-1,-1,-1,-1,2,4,6,8,-1,-1,-1,-1,2,4,6,8};
_declspec(selectany) SHORT arrStepsTable[] = {
        7,     8,     9,     10,    11,    12,     13,    14,    16,
        17,    19,    21,    23,    25,    28,     31,    34,    37,
        41,    45,    50,    55,    60,    66,     73,    80,    88,
        97,    107,   118,   130,   143,   157,    173,   190,   209,
        230,   253,   279,   307,   337,   371,    408,   449,   494,
        544,   598,   658,   724,   796,   876,    963,   1060,  1166,
        1282,  1411,  1552,  1707,  1878,  2066,   2272,  2499,  2749,
        3024,  3327,  3660,  4026,  4428,  4871,   5358,  5894,  6484,
        7132,  7845,  8630,  9493,  10442, 11487,  12635, 13899, 15289,
        16818, 18500, 20350, 22385, 24623, 27086,  29794, 32767 };	

inline DWORD Codec_IMA2PCM(const BYTE* pIMABuffer, DWORD nBufferSize, chByteArray& arrPCM)
{
	int nPackets = nBufferSize / 256;
	arrPCM.SetArraySize(nPackets * 1010);
	
	int iIndex;
	for(iIndex = 0; iIndex < nPackets; iIndex++)
	{
		SHORT* pPCMData = (SHORT*)(arrPCM.GetData() + iIndex * 1010);
		const BYTE* pIMAData = pIMABuffer + iIndex * 256;
		
		*(pPCMData++) = *(SHORT*)pIMAData;
		
		SHORT nSample = *(SHORT*)pIMAData;
		SHORT iSeed = *(SHORT*)(pIMAData+2);
		bool bOdd = true;
		int i = 0;
		for(i = 4; i < 256;)
		{
			BYTE code = bOdd ? pIMAData[i] & 0x0F : pIMAData[i] >> 4;
			SHORT step = arrStepsTable[iSeed];
			SHORT diff = step >> 3;
			if(code & 1) diff += step >> 2;
			if(code & 2) diff += step >> 1;
			if(code & 4) diff += step;
			if(code & 8)
				nSample -= diff;
			else
				nSample += diff;

			iSeed = iSeed + arrIndexAdjust[code];
			iSeed = chMAX(0, iSeed);
			iSeed = chMIN(88, iSeed);

			*(pPCMData++) = nSample;
			bOdd = !bOdd;
			if(bOdd)
				i++;
		}
	}
	return nPackets * 1010;
}

inline DWORD Codec_PCM2IMA(const BYTE* pPCMBuffer, DWORD nBufferSize, chByteArray& arrIMA)
{
	static SHORT iSeed = 0;
	int nPackets = nBufferSize / 1010;
	arrIMA.SetArraySize(nPackets * 256);
	
	bool bOdd = true;
	int iIndex = 0;
	for(iIndex = 0; iIndex < nPackets; iIndex++)
	{
		const BYTE* pPCMData = pPCMBuffer + iIndex * 1010;
		BYTE* pIMAData = arrIMA.GetData() + iIndex * 256;
		
		pIMAData[0] = pPCMData[0];
		pIMAData[1] = pPCMData[1];
		*(SHORT*)(pIMAData+2) = iSeed;
		pIMAData += 4;

		SHORT nSample = *(SHORT*)pPCMData;
		int i = 0;
		for(i = 2; i < 1010; i+=2)
		{
			SHORT diff = *(SHORT*)(pPCMData+i) - nSample;
			BYTE code = 0;
			if(diff < 0)
			{
				diff = -diff;
				code = 8;
			}

			SHORT step = arrStepsTable[iSeed];
			SHORT effdiff = step >> 3;
			if(diff >= step)
			{
				code |= 4;
				diff -= step;
				effdiff += step;
			}
			step >>= 1;
			if(diff >= step)
			{
				code |= 2;
				diff -= step;
				effdiff += step;
			}
			step >>= 1;
			if(diff >= step)
			{
				code |= 1;
				effdiff += step;
			}

			if(code & 8)
				nSample = nSample - effdiff;
			else
				nSample = nSample + effdiff;

			iSeed += arrIndexAdjust[code];
			iSeed = chMAX(0, iSeed);
			iSeed = chMIN(88, iSeed);

			if(bOdd)
				*pIMAData = code;
			else
				*(pIMAData++) = (code << 4) | (*pIMAData);
			bOdd = !bOdd;
		}
	}
	return nPackets * 256;
}

#endif// __WAVE_CODEC_H__